/* net_epoll.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <netinet/in.h>

#define PORT 4321
#define MAX_QUE_CONN_NM 5
#define EPOLL_SIZE 10       //epoll监听的客户端的最大数目，Linux2.6.8之后该参数可以忽略
#define BUFFER_SIZE 1024

int main()
{
	struct sockaddr_in server_sockaddr, client_sockaddr;
	int sin_size, count;
	int epfd;
    struct epoll_event ev, events[EPOLL_SIZE];
    int event_cnt;
	int sockfd, client_fd;
	char buf[BUFFER_SIZE];
	//创建socket
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket");
		exit(1);
	}
	//设置IP,端口号
	server_sockaddr.sin_family = AF_INET;
	server_sockaddr.sin_port = htons(PORT);
	server_sockaddr.sin_addr.s_addr = INADDR_ANY;  //任意地址，也就是表示本机的所有IP
	bzero(&(server_sockaddr.sin_zero), 8);
	int i = 1;/* 允许重复使用本地地址与套接字进行绑定 */
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
	//绑定
	if (bind(sockfd, (struct sockaddr *)&server_sockaddr,
	sizeof(struct sockaddr)) == -1)
	{
		perror("bind");
		exit(1);
	}
	//开始监听
	if(listen(sockfd, MAX_QUE_CONN_NM) == -1)
	{
		perror("listen");
		exit(1);
	}
	
	printf("listening....\n");
	
    //创建一个epoll描述符，并将监听socket加入epoll
	if((epfd = epoll_create(EPOLL_SIZE)) == -1)
    {
        perror("epoll_create");
		exit(1);
    }
    else
    {
        ev.events = EPOLLIN | EPOLLOUT; //读写事件
        ev.data.fd = sockfd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
    }
    
	while(1)
	{
		sin_size = sizeof(struct sockaddr_in);
		memset(buf, 0, sizeof(buf));
		/*调用 epoll_wait()等待事件到来*/
		if ((event_cnt = epoll_wait(epfd, events, EPOLL_SIZE, -1)) <= 0)
		{
			perror("epoll_wait");
		}
		
		for (i = 0; i < event_cnt; i++)
		{
            //判断来事件的是否是监听连接的socket
            if (events[i].data.fd == sockfd)
            { /* 服务端接收客户端的连接请求 */
                if ((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, (socklen_t *)&sin_size))== -1)
                {
                    perror("accept");
                    exit(1);
                }
                //将新连接的socket放进去
                ev.data.fd = client_fd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &ev);
                printf("New connection from %d(socket)\n", client_fd);
            }
            else /* 处理从客户端发来的消息 */
            {
                if ((count = recv(events[i].data.fd, buf, BUFFER_SIZE, 0)) > 0)
                {
                    printf("Received a message from %d: %s\n",
                    events[i].data.fd, buf);
                }
                else
                {
                    close(events[i].data.fd);
                    ev.data.fd = events[i].data.fd;
                    epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
                    printf("Client %d(socket) has left\n", events[i].data.fd);
                }
            }
		} /* end of for fd*/
	} /* end if while while*/
	
	close(sockfd);
	exit(0);
}